package com.github.data_manage.service

import com.github.data_manage.config.FUND_NO_DATA_EXCEPTION
import com.github.data_manage.config.SystemException
import com.github.data_manage.dao.ContrastBusinessDataDao
import com.github.data_manage.dao.ContrastDao
import com.github.data_manage.dao.ContrastNavDataDao
import com.github.data_manage.dao.ContrastReturnDataDao
import com.github.data_manage.domain.dto.QuarterAnnualizedResult
import com.github.data_manage.domain.dto.QuarterAnnualizedResults
import com.github.data_manage.domain.dto.YearAnnualizedResult
import com.github.data_manage.domain.dto.YearAnnualizedResults
import com.github.data_manage.domain.dto.YearQuarterAnnualizedResult
import com.github.data_manage.domain.po.*
import com.github.data_manage.domain.vo.AnalysisVo
import com.github.data_manage.util.DataUtils
import com.github.data_manage.util.DataUtils.standardDeviation
import com.github.data_manage.util.DataUtils.nav
import com.github.data_manage.util.DataUtils.ytd
import com.github.data_manage.util.DateTimeUtils.Companion.atEndOfMonth
import com.github.data_manage.util.DateTimeUtils.Companion.quarter
import org.slf4j.LoggerFactory
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.LocalDate
import java.util.TreeMap

@Service
@SuppressWarnings("all")
class ContrastService(
    private val contrastBusinessDataDao: ContrastBusinessDataDao,
    private val contrastNavDataDao: ContrastNavDataDao,
    private val contrastReturnDataDao: ContrastReturnDataDao,
    private val contrastDao: ContrastDao,
) {

    companion object {
        private val log = LoggerFactory.getLogger(ContrastService::class.java)
    }

    /**
     * 计算整个指数的nav
     */
    fun calcContrastNavData(contrastId: Int) {
        contrastNavDataDao.deleteAllByContrastId(contrastId)
        val contrastAllBusinessData = contrastBusinessDataDao.findAllByContrastId(contrastId)
        if (contrastAllBusinessData.isEmpty()) {
            return
        }
        // 起始值为最小数据月 - 1,设置为100
        val firstContrastBusinessData = contrastAllBusinessData.first()

        val startNavData = contrastNavDataDao.findContrastNavDataByContrastIdAndTime(
            contrastId,
            firstContrastBusinessData.time!!.minusMonths(1).atEndOfMonth()
        ) ?: ContrastNavData()
        startNavData.contrastId = contrastId
        startNavData.contrastName = firstContrastBusinessData.contrastName
        startNavData.time = firstContrastBusinessData.time!!.minusMonths(1).atEndOfMonth()
        startNavData.value = BigDecimal.valueOf(100)
        contrastNavDataDao.save(startNavData)

        for (contrastBusinessData in contrastAllBusinessData) {

            // 计算NAV
            val lastNavData =
                contrastNavDataDao.findLastNavDataByContrastId(
                    contrastId,
                    contrastBusinessData.time!!.minusMonths(1).atEndOfMonth()
                )!!
            val contrastNavData =
                contrastNavDataDao.findContrastNavDataByContrastIdAndTime(contrastId, contrastBusinessData.time!!)
                    ?: ContrastNavData()

            contrastNavData.contrastId = contrastId
            contrastNavData.contrastName = contrastBusinessData.contrastName
            contrastNavData.time = contrastBusinessData.time
            //            val lastBusinessData = contrastBusinessDataList[index - 1]
            contrastNavData.value = lastNavData.value * (BigDecimal.ONE + contrastBusinessData.value.divide(
                BigDecimal.valueOf(100).setScale(2, RoundingMode.UP)
            )).setScale(
                4,
                RoundingMode.HALF_UP
            )

            contrastNavDataDao.save(contrastNavData)
        }
    }

    /**
     * 计算指数指定时间段内的nav
     */
    fun calcContrastNavData(
        contrast: Contrast,
        contrastAllBusinessData: List<ContrastBusinessData>,
    ): List<ContrastNavData> {
        if (contrastAllBusinessData.isEmpty()) {
            return emptyList()
        }
        val sortedBusinessData = ArrayList<ContrastBusinessData>(contrastAllBusinessData).sortedBy { it.time!! }
        val contrastNavDataMap = TreeMap<LocalDate, ContrastNavData>()
        val startNavData = ContrastNavData()
        startNavData.contrastId = contrast.id
        startNavData.contrastName = contrast.name
        startNavData.time = sortedBusinessData.first().time!!.minusMonths(1).atEndOfMonth()
        startNavData.value = BigDecimal.valueOf(100)
        contrastNavDataMap[startNavData.time!!] = startNavData

        for (contrastBusinessData in sortedBusinessData) {

            // 计算NAV
            val contrastNavData = ContrastNavData()

            try {// 计算NAV
                val lastNavData =
                    contrastNavDataMap[contrastBusinessData.time!!.minusMonths(1).atEndOfMonth()]
                        ?: contrastNavDataMap.lastEntry().value

                contrastNavData.contrastId = contrast.id
                contrastNavData.contrastName = contrast.name
                contrastNavData.time = contrastBusinessData.time
                contrastNavData.value = (lastNavData.value * (BigDecimal.ONE + contrastBusinessData.value.divide(
                    BigDecimal.valueOf(100).setScale(2, RoundingMode.UP)
                ))).setScale(
                    2,
                    RoundingMode.HALF_UP
                )
            } catch (e: Exception) {
                log.error("异常时间是{}", contrastBusinessData.time!!.minusMonths(1).atEndOfMonth(), e)
            }

            contrastNavDataMap[contrastNavData.time!!] = contrastNavData
        }
        return contrastNavDataMap.values.toList().sortedByDescending { it.time }
    }

    /**
     * 计算指数的回撤信息
     */
    fun calcContrastReturnData(contrastId: Int) {
        contrastReturnDataDao.deleteAllByContrastId(contrastId)
        for (contrastBusinessData in contrastBusinessDataDao.findAllByContrastId(contrastId)) {
            val allContrastNavData =
                contrastNavDataDao.findAllByContrastIdAndTime(contrastId, contrastBusinessData.time!!.atEndOfMonth())
            if (allContrastNavData.isEmpty()) {
                log.error("数据内容不正确")
                return
            }
            val currentContrastNavData = allContrastNavData.last()
            val maxContrastNavValue = allContrastNavData.maxOfOrNull { it.value } ?: BigDecimal.valueOf(100)
            val contrastReturnData =
                contrastReturnDataDao.findByContrastIdAndTime(contrastId, contrastBusinessData.time!!.atEndOfMonth())
                    ?: ContrastReturnData()
            contrastReturnData.contrastId = contrastId
            contrastReturnData.contrastName = contrastBusinessData.contrastName
            contrastReturnData.time = contrastBusinessData.time
            contrastReturnData.value =
                ((currentContrastNavData.value.divide(
                    maxContrastNavValue,
                    4,
                    RoundingMode.HALF_UP
                )) - BigDecimal.ONE).multiply(
                    BigDecimal.valueOf(100)
                )
                    .setScale(
                        4, RoundingMode.HALF_UP
                    )
            contrastReturnDataDao.save(contrastReturnData)
        }
    }

    fun calcContrastReturnData(
        contrast: Contrast,
        contrastAllBusinessData: List<ContrastBusinessData>,
    ): List<ContrastReturnData> {
        return calcContrastReturnData(
            contrast,
            contrastAllBusinessData,
            calcContrastNavData(contrast, contrastAllBusinessData)
        )
    }

    /**
     * 计算指数指定时间段内的回撤信息
     */
    fun calcContrastReturnData(
        contrast: Contrast,
        contrastAllBusinessData: List<ContrastBusinessData>,
        contrastAllNavData: List<ContrastNavData>,
    ): List<ContrastReturnData> {
        val sortedBusinessData = ArrayList<ContrastBusinessData>(contrastAllBusinessData).sortedBy { it.time!! }
        return sortedBusinessData.map { contrastBusinessData ->
            val allContrastNavData =
                contrastAllNavData.filter { it.time!! <= contrastBusinessData.time!! }.sortedBy { it.time }
            if (allContrastNavData.isEmpty()) {
                return emptyList()
            }
            val currentContrastNavData = allContrastNavData.last()
            val maxContrastNavValue = allContrastNavData.maxOfOrNull { it.value } ?: BigDecimal.valueOf(100)
            val contrastReturnData = ContrastReturnData()
            contrastReturnData.contrastId = contrast.id
            contrastReturnData.contrastName = contrastBusinessData.contrastName
            contrastReturnData.time = contrastBusinessData.time
            contrastReturnData.value =
                ((currentContrastNavData.value.divide(
                    maxContrastNavValue,
                    4,
                    RoundingMode.HALF_UP
                )) - BigDecimal.ONE).multiply(
                    BigDecimal.valueOf(100)
                )
                    .setScale(
                        4, RoundingMode.HALF_UP
                    )
            contrastReturnData
        }
    }

    fun contrastAnalysis(
        contrastId: Int,
        startDate: LocalDate,
        endDate: LocalDate,
        pearsonCorrelationReturnData: List<BigDecimal> = emptyList()
    ): AnalysisVo {
        val contrast = contrastDao.findById(contrastId).orElse(null) ?: throw SystemException("指数不存在")
        val allBusinessData = contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startDate, endDate)
            .sortedBy { it.time!! }
        if (allBusinessData.isEmpty()) {
            throw SystemException("基金无数据", FUND_NO_DATA_EXCEPTION)
        }
        val contrastNavData = calcContrastNavData(contrast, allBusinessData)
        val contrastReturnData = calcContrastReturnData(contrast, allBusinessData, contrastNavData)
        val contrastAnalysisVo = AnalysisVo()
        contrastAnalysisVo.name = contrast.name
        val currentYear = endDate.year - 1
        contrastAnalysisVo.currentYear = currentYear

        val currentContrastNav =  contrastNavData.first()
        val currentContrastNavValue = currentContrastNav.value.setScale(4, RoundingMode.HALF_UP)
        contrastAnalysisVo.currentMonthNav = currentContrastNavValue
        contrastAnalysisVo.currentMonthRequite = allBusinessData.first().value.setScale(4, RoundingMode.HALF_UP)
//            allBusinessData.filter { it.time!!.year == currentYear }.maxByOrNull { it.time!! }?.value?.setScale(
//                4,
//                RoundingMode.HALF_UP
//            )

        contrastAnalysisVo.currentYearNav = allBusinessData.filter { it.time!!.year == currentYear }
            .map { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) }.ytd()

        val lastYear = currentYear - 1
        contrastAnalysisVo.lastYear = lastYear
        contrastAnalysisVo.lastYearNav = allBusinessData.filter { it.time!!.year == lastYear }
            .map { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) }.ytd()

        val beforeLastYear = currentYear - 2

        contrastAnalysisVo.beforeLastYear = beforeLastYear
        contrastAnalysisVo.beforeLastYearNav =
            allBusinessData.filter { it.time!!.year == beforeLastYear }
                .map { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) }.ytd()

        contrastAnalysisVo.yearNav =
            allBusinessData.map { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) }.nav()
        contrastAnalysisVo.yearStandardDeviation = if (allBusinessData.isEmpty()) {
            BigDecimal.ZERO
        } else {
            allBusinessData.map { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) }
                .standardDeviation()
        }
        contrastAnalysisVo.maxMonthNav = allBusinessData.maxOfOrNull { it.value } ?: BigDecimal.ZERO
        contrastAnalysisVo.minMonthNav = allBusinessData.minOfOrNull { it.value } ?: BigDecimal.ZERO
        contrastAnalysisVo.positiveNavPercent =
            BigDecimal.valueOf(allBusinessData.count { it.value.compareTo(BigDecimal.ZERO) > 0 }
                .toDouble() / allBusinessData.size).setScale(4, RoundingMode.DOWN)
                .multiply(BigDecimal.valueOf(100))
        contrastAnalysisVo.sharpRatio = if (contrastAnalysisVo.yearStandardDeviation.compareTo(BigDecimal.ZERO) == 0) {
            BigDecimal.ZERO
        } else {
            (contrastAnalysisVo.yearNav.subtract(BigDecimal.valueOf(4))).divide(
                contrastAnalysisVo.yearStandardDeviation,
                4,
                RoundingMode.HALF_UP
            )
        }

        contrastAnalysisVo.maxReturn = contrastReturnData.minOfOrNull { it.value } ?: BigDecimal.ZERO
        contrastAnalysisVo.type = 2
        contrastAnalysisVo.correlation = if (pearsonCorrelationReturnData.isEmpty()) {
            null
        } else {
            DataUtils.pearsonCorrelation(pearsonCorrelationReturnData, contrastReturnData.map { it.value })
        }
        contrastAnalysisVo.allReturnData = contrastReturnData.map { it.value }
        return contrastAnalysisVo
    }

    /**
     * 基金年化业绩
     *
     */
    fun contrastYearAnnualizedResults(
        contrastId: Int,
        startDate: LocalDate,
        endDate: LocalDate,
    ): YearAnnualizedResults {
        val contrast = contrastDao.findByIdOrNull(contrastId) ?: throw SystemException("指定id的基金未找到")
        val contrastBusinessData =
            contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startDate, endDate)
        val yearInfos = contrastBusinessData.groupBy { it.time!!.year }
            .entries.map { entry ->
                val (year, yearBusinessData) = entry
                // 总复利收益=(1+r1)×(1+r2)×⋯×(1+rn),ri为每个月的收益
                val totalValue = yearBusinessData.map {
                    it.value.divide(BigDecimal.valueOf(100), RoundingMode.HALF_UP).add(BigDecimal.ONE)
                }
                    .reduce { a, b -> a * b }
                // R年化 =(总复利收益) 的 12/n 次方 −1
                val yearValue = totalValue.pow(12 / yearBusinessData.size).subtract(BigDecimal.ONE)
                    .setScale(2, RoundingMode.HALF_UP)
                YearAnnualizedResult(year, yearValue)
            }
        return YearAnnualizedResults(contrastId, contrast.name, 2, yearInfos)
    }

    /**
     * 基金年度业绩
     */
    fun contrastYearAnnualResults(
        contrastId: Int,
        startDate: LocalDate,
        endDate: LocalDate,
    ): YearAnnualizedResults {
        val contrast = contrastDao.findByIdOrNull(contrastId) ?: throw SystemException("指定id的基金未找到")
        val contrastBusinessData =
            contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startDate, endDate)
        val yearInfos = contrastBusinessData.groupBy { it.time!!.year }
            .entries.map { entry ->
                val (year, yearBusinessData) = entry
                // 总复利收益=(1+r1)×(1+r2)×⋯×(1+rn),ri为每个月的收益
                val totalValue = yearBusinessData.map {
                    it.value.divide(BigDecimal.valueOf(100), RoundingMode.HALF_UP).add(BigDecimal.ONE)
                }
                    .reduce { a, b -> a * b }
                // R年化 =(总复利收益) 的 12/n 次方 −1
                val yearValue = totalValue.subtract(BigDecimal.ONE)
                    .setScale(2, RoundingMode.HALF_UP)
                YearAnnualizedResult(year, yearValue)
            }
        return YearAnnualizedResults(contrastId, contrast.name, 2, yearInfos)
    }

    /**
     * 基金季度化业绩
     */
    fun contrastQuarterAnnualizedResults(
        contrastId: Int,
        startDate: LocalDate,
        endDate: LocalDate,
    ): QuarterAnnualizedResults {
        val contrast = contrastDao.findByIdOrNull(contrastId) ?: throw SystemException("指定id的基金未找到")
        val contrastBusinessData =
            contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startDate, endDate)
        val yearInfos = contrastBusinessData.groupBy { it.time!!.year }
            .entries.map { entry ->
                val (year, yearBusinessData) = entry
                var index = 0
                val quarters = yearBusinessData.groupBy { it.time!!.quarter() }
                    .map { entry ->
                        val quarterBusinessData = entry.value
                        index++
                        if (quarterBusinessData.size < 3) {
                            return@map QuarterAnnualizedResult(index, null)
                        }
                        // 总复利收益=(1+r1)×(1+r2)×⋯×(1+rn),ri为每个月的收益
                        val totalValue = quarterBusinessData.map {
                            it.value.divide(BigDecimal.valueOf(100), RoundingMode.HALF_UP).add(BigDecimal.ONE)
                        }
                            .reduce { a, b -> a * b }
                        // R年化 =(总复利收益) 的 12/n 次方 −1
                        val quarterValue = totalValue.subtract(BigDecimal.ONE)
                            .setScale(2, RoundingMode.HALF_UP)
                        return@map QuarterAnnualizedResult(index, quarterValue)
                    }
                YearQuarterAnnualizedResult(year, quarters)
            }
        return QuarterAnnualizedResults(contrastId, contrast.name, 2, yearInfos)
    }
}
